40장 이벤트
이벤트 드리븐 프로그래밍
브라우저는 처리해야 할 특정 사건이 발생하면 이를 감지해서 이벤트를 발생시킨다.
이벤트가 발생했을 때 호출될 함수를 브라우저에게 알려 호출을 위임한다. 이때 호출될 함수를 이벤트 핸들러라고하고 함수를 등록하는 것을 이벤트 핸들러 등록이라고 한다.
<!DOCTYPE html>
<html>
<body>
<button>Click me!</button>
<script>
const $button = document.querySelector('button');
// 사용자가 버튼을 클릭하면 함수를 호출하도록 요청
$button.onclick = () => { alert('button click'); };
</script>
</body>
</html>
이벤트와 그에 대응하는 함수를 통해 사용자와 애플리케이션이 상호작용을 할 수 있다.
이벤트 중심으로 제어하는 프로그래밍 방식을 이벤트 드리븐 프로그래밍 이라고 한다.
이벤트 핸들러 등록
크게 3가지 방법이 있다.
이벤트 핸들러 어트리뷰트 방식
HTML 요소의 어트리뷰트 중에서 이벤트에 대응하는 이벤트 핸들러 어트리뷰트에 이벤트를 등록하는 방법이다.
<!DOCTYPE html>
<html>
<body>
<button onclick="sayHi('Lee')">Click me!</button>
<script>
function sayHi(name) {
console.log(`Hi! ${name}.`);
}
</script>
</body>
</html>
⚠️ 이벤트 핸들로 어트리뷰트 값으로 함수 참조가 아니라 함수 호출문등의 문을 할당한다.
위 코드에서는 함수 호출문을 할당했다. 이때 이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생설될 이벤트 핸들러의 함수 몸체를 의미한다.
즉 아래와 같은 함수를 암묵적으로 생성하고 이벤트 핸들러 어튜리뷰트 이름과 똑같은 키 onclick 이벤트 핸들러 프로퍼티에 할당.
function onclick(event) {
sayHi('Lee');
}
이렇게 동작하는 이유는 이벤트 핸들러에 인수를 전달하기 위해서 이다.
이벤트 핸들러 프로퍼티 방식
DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가지고 있다.
이벤트 프로퍼티에 함수를 바인딩하면 이벤트 핸들러가 등록된다.
<!DOCTYPE html>
<html>
<body>
<button>Click me!</button>
<script>
const $button = document.querySelector('button');
// 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩
$button.onclick = function () {
console.log('button click');
};
</script>
</body>
</html>
이벤트 발생시킬 객체인 이벤트 타깃과 이벤트의 종류를 나타내는 문자열인 이벤트 타입 그리고 이벤트 핸들러를 지정해아한다.
이벤트 핸들러는 이벤트 타깃이나 전파된 이벤트 캐리할 DOM 노드 객체에 바인딩한다.
👎 이벤트 핸들러 프로퍼티에 하나의 이벤트만 바인딩할 수 있다는 단점이 있음.
addEventListener 메서드 방식
addEventListener 메서드는 하나 이상의 이벤트 핸들러 등록할 수 있다. 여러개면 등록된 순서대로 호출.
<!DOCTYPE html>
<html>
<body>
<button>Click me!</button>
<script>
const $button = document.querySelector('button');
// addEventListener 메서드는 동일한 요소에서 발생한 동일한 이벤트에 대해
// 하나 이상의 이벤트 핸들러를 등록할 수 있다.
$button.addEventListener('click', function () {
console.log('[1]button click');
});
$button.addEventListener('click', function () {
console.log('[2]button click');
});
</script>
</body>
</html>
단, 참조가 동일한 이벤트 핸들러는 중복 등록해도 하나만 등록된다.
이벤트 핸들러 제거
이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러는 해당 프로퍼티에 null을 할당해서 제거한다.
addEventListener 메서드로 등록한 이벤트 핸들러는 EvenTarget.prototype.removeEventListener
메서드 사용해서 제거할 수 있다.
전달하는 인수가 동일해야 한다.
<!DOCTYPE html>
<html>
<body>
<button>Click me!</button>
<script>
const $button = document.querySelector('button');
const handleClick = () => console.log('button click');
// 이벤트 핸들러 등록
$button.addEventListener('click', handleClick);
// 이벤트 핸들러 제거
// addEventListener 메서드에 전달한 인수와 removeEventListener 메서드에
// 전달한 인수가 일치하지 않으면 이벤트 핸들러가 제거되지 않는다.
$button.removeEventListener('click', handleClick, true); // 실패
$button.removeEventListener('click', handleClick); // 성공
</script>
</body>
</html>
전달 인수가 동일해야 해서 무명함수는 제거할 수가 없음
단, 내부에서 arguments.callee 사용해서 제거할 수 있기는 함.
그냥 이벤트 핸들러의 참조를 변수에 저장하고 제거해라.
이벤트 객체
이벤트가 발생하면 이벤트에 관련한 정보를 담고 있는 이벤트 객체가 동적으로 생성된다.
생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다.
흔히 e로 전달받는 객체 말하는 듯.
<!DOCTYPE html>
<html>
<body>
<p>클릭하세요. 클릭한 곳의 좌표가 표시됩니다.</p>
<em class="message"></em>
<script>
const $msg = document.querySelector('.message');
// 클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다.
function showCoords(e) {
$msg.textContent = `clientX: ${e.clientX}, clientY: ${e.clientY}`;
}
document.onclick = showCoords;
</script>
</body>
</html>
이벤트 핸들러 어트리뷰트 방식으로 등록했으면 event를 통해서만 전달 받을 수 있다.
<!DOCTYPE html>
<html>
<head>
<style>
html, body { height: 100%; }
</style>
</head>
<!-- 이벤트 핸들러 어트리뷰트 방식의 경우 event가 아닌 다른 이름으로는 이벤트 객체를
전달받지 못한다. -->
<body onclick="showCoords(event)">
<p>클릭하세요. 클릭한 곳의 좌표가 표시됩니다.</p>
<em class="message"></em>
<script>
const $msg = document.querySelector('.message');
// 클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다.
function showCoords(e) {
$msg.textContent = `clientX: ${e.clientX}, clientY: ${e.clientY}`;
}
</script>
</body>
</html>
왜 why?
이벤트 핸들러 어트리뷰트 방식은 암묵적으로 이벤트 함수를 생성하여 프로퍼티에 할당하기 때문이다. 그 과정에서 암묵적으로 event로 명명해서.
function onclick(event) {
showCoords(event);
}
이벤트 객체의 상속 구조
Event 인터페이스에는 모든 이벤트 객체의 공통 프로퍼티가 정의되어 있고, 하위 인터페이스에는 이벤트 타입에 따라 고유한 프로퍼티가 정의되어 있다.
이벤트 객체의 공통 프로퍼티
Evnet.prototype 에 정의되어 있는 공통 프로퍼티들
- type : 이벤트 타입
- target : 이벤트를 발생시킨 DOM 요소
- currentTarget : 이벤트 핸들러가 바인딩된 DOM 요소
- eventPhase : 이벤트 전파단계 (0 : 이벤트 없음, 1:캡처링 단계, 2: 타깃 단계, 3:버블링단계)
- bubbles : 이벤트를 버블링으로 전파하는지 여부.
- cancelable : preventDefault 메서드를 호출하여 이벤트의 기본 동작을 취소할수 있는지 여부.
- defaultPrevented : preventDefault 메서드를 호출하여 이벤트를 취소했는지 여부.
- isTrusted : 사용자의 행위에 의해 발생한 이벤트인지 여부.
- timestamp : 이벤트가 발생한 시각
일반적으로 target 프로퍼티와 currentTarget 프로퍼티가 동일한 객체를 가리킨다.
이벤트 전파
DOM 요소 노드에서 발생한 이벤트는 DOM 트리를 통해서 전파된다. 이를 이벤트 전파
라고 한다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li id="apple">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
</ul>
</body>
</html>
- 캡처링 단계 : 이벤트가 상위 요소에서 하위 요소 방향으로 전파
- 타깃 단계 : 이벤트가 이벤트 타깃에 도달
- 버블링 단계 : 이벤트가 하위 요소에서 상위 요소 방향으로 전파
li#banana 클릭해서 이벤트 발생 → 이벤트 객체 생성 ( 타깃이 li#banana)
→ 이벤트 타겟 찾아서 window 에서 시작해서 타깃 방향으로 전파 캡처링
→ 이벤트 객체가 타깃에 도달 타깃 단계
→ 이벤트 타깃에서 시작해서 window 방향으로 전파 버블링
이벤트 핸들러 어튜리뷰트/프로퍼티 방식은 타깃 단계와 버블링 단계의 이벤트만 캐치할 수 있음.
addEventListener 메서드 방식은 모든 단계의 이벤트 선별적으로 캐치할 수 있다. 캡처링 이벤트 캐치하려면 3번째 인수 true로 전달하면 된다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li id="apple">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
</ul>
<script>
const $fruits = document.getElementById('fruits');
const $banana = document.getElementById('banana');
// \#fruits 요소의 하위 요소인 li 요소를 클릭한 경우
// 캡처링 단계의 이벤트를 캐치한다.
$fruits.addEventListener('click', e => {
console.log(`이벤트 단계: ${e.eventPhase}`); // 1: 캡처링 단계
console.log(`이벤트 타깃: ${e.target}`); // [object HTMLLIElement]
console.log(`커런트 타깃: ${e.currentTarget}`); // [object HTMLUListElement]
}, true);
// 타깃 단계의 이벤트를 캐치한다.
$banana.addEventListener('click', e => {
console.log(`이벤트 단계: ${e.eventPhase}`); // 2: 타깃 단계
console.log(`이벤트 타깃: ${e.target}`); // [object HTMLLIElement]
console.log(`커런트 타깃: ${e.currentTarget}`); // [object HTMLLIElement]
});
// 버블링 단계의 이벤트를 캐치한다.
$fruits.addEventListener('click', e => {
console.log(`이벤트 단계: ${e.eventPhase}`); // 3: 버블링 단계
console.log(`이벤트 타깃: ${e.target}`); // [object HTMLLIElement]
console.log(`커런트 타깃: ${e.currentTarget}`); // [object HTMLUListElement]
});
</script>
</body>
</html>
이벤트는 이벤트를 발생시킨 이벤트 타깃은 물론 상위 DOM 요소에서도 캐치할 수 있다.
대부분의 이벤트는 캡처링과 버블링을 통해 전파되지만, 버블링되지 않는 이벤트도 있다.
얘네들은 event.bubbles 값이 false임.
- 포커스 이벤트 : focus/blur
- 리소스 이벤트 : load/unload/abort/error
- 마우스 이벤트 : mouseente/mouseleave
이벤트 위임
li 요소가 여러개 이면 모두 각각에 이벤트를 등록해야할까?
→ 이벤트 위임을 통해 해결하자!
이벤트 위임
은 여러개의 하위 DOM 요소 각각에 이벤트를 등록하는 대신 상위 DOM 요소 하나에 이벤트 핸들러 등록하는 방법이다.
상위 DOM 요소에서도 이벤트를 캐치할 수 있기 때문.
상위 요소에 이벤트 핸들러를 등록하기 때문에 이벤트 타깃이 원하는 DOM 요소가 아닐 수도 있어서 이벤트 타깃을 검사할 필요가 있다.
element.prototype.matches
메서드 사용
<!DOCTYPE html>
<html>
<head>
<style>
\#fruits {
display: flex;
list-style-type: none;
padding: 0;
}
\#fruits li {
width: 100px;
cursor: pointer;
}
\#fruits .active {
color: red;
text-decoration: underline;
}
</style>
</head>
<body>
<nav>
<ul id="fruits">
<li id="apple" class="active">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
</ul>
</nav>
<div>선택된 내비게이션 아이템: <em class="msg">apple</em></div>
<script>
const $fruits = document.getElementById("fruits");
const $msg = document.querySelector(".msg");
// 사용자 클릭에 의해 선택된 내비게이션 아이템(li 요소)에 active 클래스를 추가하고
// 그 외의 모든 내비게이션 아이템의 active 클래스를 제거한다.
function activate({ target }) {
console.log(target);
// 이벤트를 발생시킨 요소(target)가 ul\#fruits의 자식 요소가 아니라면 무시한다.
if (!target.matches("\#fruits > li")) return;
[...$fruits.children].forEach(($fruit) => {
$fruit.classList.toggle("active", $fruit === target);
$msg.textContent = target.id;
});
}
// 이벤트 위임: 상위 요소(ul\#fruits)는 하위 요소의 이벤트를 캐치할 수 있다.
$fruits.onclick = activate;
</script>
</body>
</html>
이벤트 객체의 currentTarget 프로퍼티는 이벤트 핸들러가 등록된 요소를 키지만, target 프로퍼티는 실제로 이벤트를 발생시킨 DOM 요소를 킨다.
여기에서는 currentTarget은 fruits 를 가르키고 target은 클릭한 li 를 가르킨다.
DOM 요소의 기본 동작 조작
DOM 요소의 기본 동작 중단
e.preventDefault
메서드 사용해서 기본 동작 중단.
<!DOCTYPE html>
<html>
<body>
<a href="https://www.google.com">go</a>
<input type="checkbox">
<script>
document.querySelector('a').onclick = e => {
// a 요소의 기본 동작을 중단한다.
e.preventDefault();
};
document.querySelector('input[type=checkbox]').onclick = e => {
// checkbox 요소의 기본 동작을 중단한다.
e.preventDefault();
};
</script>
</body>
</html>
이벤트 전파 방지
이벤트 객체의 stopPropagation
메서드 사용해서 전파 중단.
<!DOCTYPE html>
<html>
<body>
<div class="container">
<button class="btn1">Button 1</button>
<button class="btn2">Button 2</button>
<button class="btn3">Button 3</button>
</div>
<script>
// 이벤트 위임. 클릭된 하위 버튼 요소의 color를 변경한다.
document.querySelector('.container').onclick = ({ target }) => {
if (!target.matches('.container > button')) return;
target.style.color = 'red';
};
// .btn2 요소는 이벤트를 전파하지 않으므로 상위 요소에서 이벤트를 캐치할 수 없다.
document.querySelector('.btn2').onclick = e => {
e.stopPropagation(); // 이벤트 전파 중단
e.target.style.color = 'blue';
};
</script>
</body>
</html>
상위 요소에 container에 이벤트 등록했는데 btn2 는 자체적인 이벤트 핸들러를 사용해서 중단한 모습.
이벤트 핸들러 내부의 this
이벤트 핸들러 어트리뷰트 방식
이벤트 핸들러 어튜리뷰트의 값으로 지정된 문자열은 암묵적으로 생성되는 이벤트 핸들러의 문이다.
따라서, 이벤트 핸들러에 의해 일반 함수로 호출된다.
→ 내부의 this가 전역 객체를 가리킨다.
호출할 때 this 전달해서 바인딩한 DOM요소를 가르킬 수 있다.
<!DOCTYPE html>
<html>
<body>
<button onclick="handleClick(this)">Click me</button>
<script>
function handleClick(button) {
console.log(button); // 이벤트를 바인딩한 button 요소
console.log(this); // window
}
</script>
</body>
</html>
이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식
이 핸 프 방식과 aEL 방식 모두 내부의 this 가 이벤트를 바인딩한 DOM 요소를 가리킨다.
즉, 이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같다.
<!DOCTYPE html>
<html>
<body>
<button class="btn1">0</button>
<button class="btn2">0</button>
<script>
const $button1 = document.querySelector('.btn1');
const $button2 = document.querySelector('.btn2');
// 이벤트 핸들러 프로퍼티 방식
$button1.onclick = function (e) {
// this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
console.log(this); // $button1
console.log(e.currentTarget); // $button1
console.log(this === e.currentTarget); // true
// $button1의 textContent를 1 증가시킨다.
++this.textContent;
};
// addEventListener 메서드 방식
$button2.addEventListener('click', function (e) {
// this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
console.log(this); // $button2
console.log(e.currentTarget); // $button2
console.log(this === e.currentTarget); // true
// $button2의 textContent를 1 증가시킨다.
++this.textContent;
});
</script>
</body>
</html>
단, 화살표 함수로 정의한 이벤트 핸들러 내부의 this는 상위 스코프의 this를 가리킨다.
<!DOCTYPE html>
<html>
<body>
<button class="btn1">0</button>
<button class="btn2">0</button>
<script>
const $button1 = document.querySelector('.btn1');
const $button2 = document.querySelector('.btn2');
// 이벤트 핸들러 프로퍼티 방식
$button1.onclick = e => {
// 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
console.log(this); // window
console.log(e.currentTarget); // $button1
console.log(this === e.currentTarget); // false
// this는 window를 가리키므로 window.textContent에 NaN(undefined + 1)을 할당한다.
++this.textContent;
};
// addEventListener 메서드 방식
$button2.addEventListener('click', e => {
// 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
console.log(this); // window
console.log(e.currentTarget); // $button2
console.log(this === e.currentTarget); // false
// this는 window를 가리키므로 window.textContent에 NaN(undefined + 1)을 할당한다.
++this.textContent;
});
</script>
</body>
</html>
클래스에서 이벤트 핸들러를 바인딩 하는 경우, this 사용에 있어서 주의해아한다. 이벤트 핸들러 내부의 this는 이벤트 바인딩한 요소를 가리키니까.
→ bind메서드 사용해서 this전달 해서 내부의 this가 인스턴스 가르키게 따로 해줘야함.
<!DOCTYPE html>
<html>
<body>
<button class="btn">0</button>
<script>
class App {
constructor() {
this.$button = document.querySelector('.btn');
this.count = 0;
// increase 메서드를 이벤트 핸들러로 등록
this.$button.onclick = this.increase;
// 요렇게
this.$button.onclick = this.increase.bind(this);
}
increase() {
// 이벤트 핸들러 increase 내부의 this는 DOM 요소(this.$button)를 가리킨다.
// 따라서 this.$button은 this.$button.$button과 같다.
this.$button.textContent = ++this.count;
// -> TypeError: Cannot set property 'textContent' of undefined
}
}
new App();
</script>
</body>
</html>
혹은 화살표 함수를 이벤트 핸들러로 등록해서 this가 인스턴스 가르키도록 할 수 있음.
근데 이때, increase가 프로토타입 메서드가 아니라 인스턴스 메서드가 됨.
왜 why?
변수 처럼 할당해서 정의하면 클래스 필드 정의가 되어버려서
<!DOCTYPE html>
<html>
<body>
<button class="btn">0</button>
<script>
class App {
constructor() {
this.$button = document.querySelector('.btn');
this.count = 0;
// 화살표 함수인 increase를 이벤트 핸들러로 등록
this.$button.onclick = this.increase;
}
// 클래스 필드 정의
// increase는 인스턴스 메서드이며 내부의 this는 인스턴스를 가리킨다.
increase = () => this.$button.textContent = ++this.count;
}
new App();
</script>
</body>
</html>
이벤트 핸들러에 인수 전달
함수에 인수 전달하려면 호출할 때 전달해야하낟. 이벤트 핸들러 어트리뷰트 방식은 함수 호출문을 사요할 수 있어서 인수 전달할 수 있지만 다른 방식들은 이벤트 핸들러를 브라우저가 호출하기 때문에 함수 자체를 등록해야 한다. 그래서 인수를 전달할 수 없다.
근데 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달할 수 있다.
<!DOCTYPE html>
<html>
<body>
<label>User name <input type='text'></label>
<em class="message"></em>
<script>
const MIN_USER_NAME_LENGTH = 5; // 이름 최소 길이
const $input = document.querySelector('input[type=text]');
const $msg = document.querySelector('.message');
const checkUserNameLength = min => {
$msg.textContent
= $input.value.length < min ? `이름은 ${min}자 이상 입력해 주세요` : '';
};
// 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달한다.
$input.onblur = () => {
checkUserNameLength(MIN_USER_NAME_LENGTH);
};
</script>
</body>
</html>
혹은 이벤트 핸들러 반환함수 호출하면서 인수 전달
<!DOCTYPE html>
<html>
<body>
<label>User name <input type='text'></label>
<em class="message"></em>
<script>
const MIN_USER_NAME_LENGTH = 5; // 이름 최소 길이
const $input = document.querySelector('input[type=text]');
const $msg = document.querySelector('.message');
// 이벤트 핸들러를 반환하는 함수
const checkUserNameLength = min => e => {
$msg.textContent
= $input.value.length < min ? `이름은 ${min}자 이상 입력해 주세요` : '';
};
// 이벤트 핸들러를 반환하는 함수를 호출하면서 인수를 전달한다.
$input.onblur = checkUserNameLength(MIN_USER_NAME_LENGTH);
</script>
</body>
</html>
커스텀 이벤트
커스텀 이벤트 생성
이벤트 객체는 이벤트 생성자 함수로 생성할 수 있음.
이벤트가 발생해서 암묵적으로 생성되는 이벤트 객체는 이벤트 종류에 따라서 타입이 결정되지만, 이벤트 생성자 함수로 생성하면 임의의 이벤트 타입 지정 가능.
이벤트 생성자 함수는 첫번째 인수로 이벤트 타입을 나타내는 문자열을 전달받는다. 기존 이벤트 타입이나, 임의의 문자열 사용 가능.
일반적으로 customEvent 이벤트 생성자 함수 사용
// KeyboardEvent 생성자 함수로 keyup 이벤트 타입의 커스텀 이벤트 객체를 생성
const keyboardEvent = new KeyboardEvent('keyup');
console.log(keyboardEvent.type); // keyup
// CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent = new CustomEvent('foo');
console.log(customEvent.type); // foo
커스텀 이벤트 객체는 bubbles 와 cancelable 프로퍼티 값이 false가 기본
따로 객체 인수로 줘서 설정 가능
이벤트 타입에 따른 고유의 프로퍼티 값도 설정 가능.
// MouseEvent 생성자 함수로 click 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent = new MouseEvent('click', {
bubbles: true,
cancelable: true
});
console.log(customEvent.bubbles); // true
console.log(customEvent.cancelable); // true
이벤트 생성자 함수로 생성한 커스텀 이벤트는 isTrusted 프로퍼티 값이 항상 false.
// InputEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent = new InputEvent('foo');
console.log(customEvent.isTrusted); // false
커스텀 이벤트 디스패치
생성된 커스텀 이벤트는 dispatchEvent
메서드로 디스패치
(이벤트를 발생시키는 행위) 할 수 있다.
이벤트 객체를 인수로 전달하면서 호출하면 해당 타입의 이벤트가 발생
<!DOCTYPE html>
<html>
<body>
<button class="btn">Click me</button>
<script>
const $button = document.querySelector('.btn');
// 버튼 요소에 click 커스텀 이벤트 핸들러를 등록
// 커스텀 이벤트를 디스패치하기 이전에 이벤트 핸들러를 등록해야 한다.
$button.addEventListener('click', e => {
console.log(e); // MouseEvent {isTrusted: false, screenX: 0, ...}
alert(`${e} Clicked!`);
});
// 커스텀 이벤트 생성
const customEvent = new MouseEvent('click');
// 커스텀 이벤트 디스패치(동기 처리). click 이벤트가 발생한다.
$button.dispatchEvent(customEvent);
</script>
</body>
</html>
일반적인 이벤트 핸들러와 다르게 dispatchEvent
메서드는 이벤트 핸들러를 동기 처리 방식으로 호출. 그래서 dispatchEvent 메서드로 이벤트 디스패치 하기 전에 커스텀 이벤트를 처리할 이벤트 핸들로 등록해야한다.
<!DOCTYPE html>
<html>
<body>
<button class="btn">Click me</button>
<script>
const $button = document.querySelector('.btn');
// 버튼 요소에 foo 커스텀 이벤트 핸들러를 등록
// 커스텀 이벤트를 디스패치하기 이전에 이벤트 핸들러를 등록해야 한다.
$button.addEventListener('foo', e => {
// e.detail에는 CustomEvent 함수의 두 번째 인수로 전달한 정보가 담겨 있다.
alert(e.detail.message);
});
// CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent = new CustomEvent('foo', {
detail: { message: 'Hello' } // 이벤트와 함께 전달하고 싶은 정보
});
// 커스텀 이벤트 디스패치
$button.dispatchEvent(customEvent);
</script>
</body>
</html>
기존 이벤트 타입이 아니라 임의의 이벤트 타입 지정해서 커스텀 이벤트 객체 생성한 경우, addEventListener 메서드 방식으로 이벤트 핸들러 등록해야 한다.